/**
 * ApiService.js
 * Centralized service for API calls to the backend
 */

class ApiService {
  /**
   * Get the authentication token from cookies
   * @returns {string|null} The auth token or null if not found
   */
  static getAuthToken() {
    const cookies = document.cookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      if (cookie.startsWith('virtualbank_token=')) {
        return cookie.substring('virtualbank_token='.length, cookie.length);
      }
    }
    return null;
  }

  /**
   * Set the authentication token as a cookie
   * @param {string} token - The authentication token
   * @param {number} expiresInSeconds - Expiration time in seconds
   */
  static setAuthToken(token, expiresInSeconds = 7200) {
    const date = new Date();
    date.setTime(date.getTime() + expiresInSeconds * 1000);
    document.cookie = `virtualbank_token=${token}; expires=${date.toUTCString()}; path=/`;
  }

  /**
   * Clear the authentication token
   */
  static clearAuthToken() {
    document.cookie = 'virtualbank_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  }

  /**
   * Make a GET request to the API
   * @param {string} endpoint - API endpoint to call
   * @param {Object} params - URL parameters
   * @returns {Promise} The fetch promise
   */
  static async get(endpoint, params = {}) {
    const token = this.getAuthToken();
    
    // Build query string
    const queryParams = new URLSearchParams();
    Object.keys(params).forEach(key => {
      if (params[key] !== undefined && params[key] !== null) {
        queryParams.append(key, params[key]);
      }
    });
    
    const queryString = queryParams.toString();
    const url = `${endpoint}${queryString ? '?' + queryString : ''}`;
    
    try {
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          ...(token ? { 'Authorization': `Bearer ${token}` } : {})
        }
      });
      
      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ApiError(
          response.status,
          errorData.message || 'An error occurred while fetching data',
          errorData
        );
      }
      
      return await response.json();
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      } else {
        throw new ApiError(0, error.message || 'Network error', {});
      }
    }
  }

  /**
   * Make a POST request to the API
   * @param {string} endpoint - API endpoint to call
   * @param {Object} data - Request body data
   * @returns {Promise} The fetch promise
   */
  static async post(endpoint, data = {}) {
    const token = this.getAuthToken();
    
    try {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(token ? { 'Authorization': `Bearer ${token}` } : {})
        },
        body: JSON.stringify(data)
      });
      
      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ApiError(
          response.status,
          errorData.message || 'An error occurred while submitting data',
          errorData
        );
      }
      
      return await response.json();
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      } else {
        throw new ApiError(0, error.message || 'Network error', {});
      }
    }
  }

  /**
   * Make a PUT request to the API
   * @param {string} endpoint - API endpoint to call
   * @param {Object} data - Request body data
   * @returns {Promise} The fetch promise
   */
  static async put(endpoint, data = {}) {
    const token = this.getAuthToken();
    
    try {
      const response = await fetch(endpoint, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
          ...(token ? { 'Authorization': `Bearer ${token}` } : {})
        },
        body: JSON.stringify(data)
      });
      
      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ApiError(
          response.status,
          errorData.message || 'An error occurred while updating data',
          errorData
        );
      }
      
      return await response.json();
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      } else {
        throw new ApiError(0, error.message || 'Network error', {});
      }
    }
  }

  /**
   * Make a DELETE request to the API
   * @param {string} endpoint - API endpoint to call
   * @param {Object} data - Request body data (optional)
   * @returns {Promise} The fetch promise
   */
  static async delete(endpoint, data = null) {
    const token = this.getAuthToken();
    
    const options = {
      method: 'DELETE',
      headers: {
        'Content-Type': 'application/json',
        ...(token ? { 'Authorization': `Bearer ${token}` } : {})
      }
    };
    
    if (data) {
      options.body = JSON.stringify(data);
    }
    
    try {
      const response = await fetch(endpoint, options);
      
      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ApiError(
          response.status,
          errorData.message || 'An error occurred while deleting data',
          errorData
        );
      }
      
      return await response.json();
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      } else {
        throw new ApiError(0, error.message || 'Network error', {});
      }
    }
  }

  /**
   * Check if the user is authenticated
   * @returns {boolean} True if authenticated, false otherwise
   */
  static isAuthenticated() {
    return !!this.getAuthToken();
  }

  /**
   * Redirect to login page if not authenticated
   */
  static redirectIfNotAuthenticated() {
    if (!this.isAuthenticated()) {
      window.location.href = '/login.html';
      return true;
    }
    return false;
  }

  /**
   * Handle API error
   * @param {Error} error - The error object
   * @param {Function} errorCallback - Custom error handler
   */
  static handleError(error, errorCallback = null) {
    console.error('API Error:', error);
    
    // If API error is authentication related, redirect to login
    if (error instanceof ApiError && 
        (error.status === 401 || error.status === 403 || 
         error.message.includes('authentication') || 
         error.message.includes('token'))) {
      this.clearAuthToken();
      localStorage.removeItem('userInfo');
      window.location.href = '/login.html';
      return;
    }
    
    // Use custom error handler if provided
    if (typeof errorCallback === 'function') {
      errorCallback(error);
      return;
    }
    
    // Default error handling: show toast notification
    if (window.Toast) {
      window.Toast.show({
        type: 'error',
        title: 'Error',
        message: error.message || 'An error occurred. Please try again.'
      });
    } else {
      alert(error.message || 'An error occurred. Please try again.');
    }
  }
}

/**
 * Custom API Error class
 */
class ApiError extends Error {
  /**
   * Create a new API error
   * @param {number} status - HTTP status code
   * @param {string} message - Error message
   * @param {Object} data - Additional error data
   */
  constructor(status, message, data = {}) {
    super(message);
    this.name = 'ApiError';
    this.status = status;
    this.data = data;
  }
}

// Export for use in other files
window.ApiService = ApiService;
window.ApiError = ApiError;
